import { useCallback, useEffect, useRef, useState, useMemo } from 'react';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import XYZ from 'ol/source/XYZ';
import Map from 'ol/Map';
import { fromLonLat, toLonLat } from 'ol/proj';
import { Station } from '@/pages/metro/line/[id]/components/StationLine';
import { Vector as VectorLayer } from 'ol/layer';
import Feature from 'ol/Feature';
import { LineString, Point, Polygon } from 'ol/geom';
import VectorSource from 'ol/source/Vector';
import { Fill, Stroke, Style, Text } from 'ol/style';
import CircleStyle from 'ol/style/Circle';
import { Coordinate } from 'ol/coordinate';
import { LineData } from './LineHead';
import { FullScreen } from 'ol/control';
import LayerSwitcher, { GroupLayerOptions } from 'ol-layerswitcher';
import LayerGroup from 'ol/layer/Group';
import { MAP_CIA, MAP_CVA, MAP_IMG, MAP_VEC } from '@/environment';
import { t } from 'i18next';
import { Modify, Snap } from 'ol/interaction';
import { Collection } from 'ol';

interface Props {
  isPhone?: boolean;
  lines: { line: LineData; stations: Station[] }[];
  isSingle?: boolean;
  onLineEdit?: (coordinate: Coordinate[]) => void;
}

const view_url = [
  { name: t('metro.mapVEC'), url: MAP_VEC, visible: false },
  { name: t('metro.mapIMG'), url: MAP_IMG, visible: true },
  { name: t('metro.mapCVA'), url: MAP_CVA, visible: false },
  { name: t('metro.mapCIA'), url: MAP_CIA, visible: false },
];

const default_position: [number, number] = [120.34, 30.24508];
const unique_id = new Date().getTime();

const MapTianditu = (props: Props) => {
  const { lines, isPhone, isSingle, onLineEdit } = props;
  const mapElement = useRef<Map>(null);
  const modifyRef = useRef<Modify | null>(null);

  const [isEditing, setIsEditing] = useState(false);

  const view = useCallback(
    () =>
      new View({
        projection: 'EPSG:3857',
        center: fromLonLat(default_position),
        zoom: 12,
        maxZoom: 18,
        minZoom: 1,
      }),
    [],
  );

  const renderLineLayer = useCallback(
    (positions: Coordinate[], color: string, isCycle?: boolean) =>
      new VectorLayer({
        source: new VectorSource({
          features: [
            new Feature({
              geometry: new LineString(
                positions.length > 2 && isCycle ? [...positions, positions[0]] : positions,
              ),
            }),
          ],
        }),
        style: new Style({
          stroke: new Stroke({ color, width: 3, lineCap: 'round', lineJoin: 'round' }),
        }),
      }),
    [],
  );

  const renderStationLayer = useCallback(
    (position: Coordinate, color: string, name: string, en?: string) =>
      new VectorLayer({
        minZoom: 12,
        source: new VectorSource({
          features: [new Feature({ geometry: new Point(position) })],
        }),
        style: new Style({
          image: new CircleStyle({
            radius: 5,
            fill: new Fill({ color: 'white' }),
            stroke: new Stroke({ color, width: 2 }),
          }),
          text: new Text({
            text: en ? `${name}\n${en}` : name,
            font: `normal ${isPhone ? '12px' : '14px'} Arial`,
            offsetX: 0,
            offsetY: -20,
            stroke: new Stroke({ color: 'white', width: 1 }),
          }),
        }),
      }),
    [isPhone],
  );

  const baseLayer = useMemo(
    () =>
      view_url.map(
        (url) =>
          new LayerGroup({
            title: url.name,
            layers: [
              new TileLayer({
                source: new XYZ({ url: url.url }),
                opacity: 0.8,
              }),
            ],
            visible: url.visible,
          } as GroupLayerOptions),
      ),
    [],
  );

  const renderLineName = useCallback(
    (positions: Coordinate[], color: string, lineName: string) =>
      new VectorLayer({
        minZoom: 11,
        source: new VectorSource({
          features: [
            positions.length > 0
              ? new Feature({
                  geometry: new Point(positions[0]),
                  name: lineName,
                })
              : new Feature({}),
          ],
        }),
        style: new Style({
          text: new Text({
            text: lineName,
            font: 'bold 20px Arial',
            fill: new Fill({ color: '#fff' }),
            offsetX: 20,
            offsetY: 20,
            backgroundFill: new Fill({ color }),
            padding: [5, 5, 5, 5],
          }),
        }),
      }),
    [],
  );

  const metroLayer = useMemo(
    () =>
      lines.map(
        (line) =>
          new LayerGroup({
            title: line.line.cityName + line.line.lineName,
            layers: [
              renderLineLayer(
                line.line.lineRoute?.length > 1
                  ? line.line.lineRoute.map((point) => fromLonLat(point))
                  : line.stations.map((station) =>
                      fromLonLat([
                        Number(station.stationLongitude),
                        Number(station.stationLatitude),
                      ]),
                    ),
                line.line.lineColor,
                line.line.lineCycle,
              ),
              ...line.stations.map((station) =>
                renderStationLayer(
                  fromLonLat([Number(station.stationLongitude), Number(station.stationLatitude)]),
                  line.line.lineColor,
                  station.stationName,
                  station.stationEn,
                ),
              ),
              renderLineName(
                line.stations.map((station) =>
                  fromLonLat([Number(station.stationLongitude), Number(station.stationLatitude)]),
                ),
                line.line.lineColor,
                line.line.lineName,
              ),
            ],
          } as GroupLayerOptions),
      ),
    [lines, renderLineLayer, renderLineName, renderStationLayer],
  );

  const calculateMapSize = useCallback(() => {
    const longitudes = lines.flatMap((line) =>
      line.stations.map((station) => Number(station.stationLongitude)),
    );
    const latitudes = lines.flatMap((line) =>
      line.stations.map((station) => Number(station.stationLatitude)),
    );

    if (longitudes.length === 0 || latitudes.length === 0) return undefined;

    const minLon = Math.min(...longitudes);
    const maxLon = Math.max(...longitudes);
    const minLat = Math.min(...latitudes);
    const maxLat = Math.max(...latitudes);

    return new Polygon([
      [
        fromLonLat([minLon, minLat]),
        fromLonLat([minLon, maxLat]),
        fromLonLat([maxLon, maxLat]),
        fromLonLat([maxLon, minLat]),
      ],
    ]);
  }, [lines]);

  const renderMap = useCallback(() => {
    mapElement.current = new Map({
      target: `map-${unique_id}`,
      view: view(),
      layers: [...baseLayer, ...metroLayer],
      controls: [
        new FullScreen(),
        new LayerSwitcher({
          reverse: false,
          groupSelectStyle: 'group',
          startActive: lines.length > 1,
          activationMode: 'click',
        }),
      ],
    });

    const mapSize = calculateMapSize();
    if (mapSize) {
      mapElement.current.getView().fit(mapSize, { padding: [30, 30, 30, 30] });
    }
  }, [baseLayer, calculateMapSize, lines.length, metroLayer, view]);

  const toggleEditMode = useCallback(() => {
    if (!mapElement.current) return;

    if (!isEditing) {
      // 收集所有站点的坐标作为吸附点
      const stationCoordinates = lines.flatMap((line) =>
        line.stations.map((station) =>
          fromLonLat([Number(station.stationLongitude), Number(station.stationLatitude)]),
        ),
      );

      // 获取所有线段要素
      const lineFeatures = metroLayer
        .flatMap((layer) =>
          layer instanceof LayerGroup
            ? layer
                .getLayers()
                .getArray()
                .filter((l) => l instanceof VectorLayer)
            : [],
        )
        .filter((layer) => {
          if (!(layer instanceof VectorLayer)) return false;
          const features = layer.getSource()?.getFeatures();
          return features?.[0]?.getGeometry() instanceof LineString;
        })
        .flatMap((layer) => layer.getSource()?.getFeatures() || []);

      // 创建包含线段要素的集合用于编辑
      const featuresCollection = new Collection(lineFeatures);

      const modify = new Modify({
        features: featuresCollection, // 使用线段要素集合进行编辑
        pixelTolerance: 10,
        hitDetection: true,
      });

      // 创建只包含站点的要素集合用于吸附
      const stationFeatures = stationCoordinates.map(
        (coord) => new Feature({ geometry: new Point(coord) }),
      );
      const stationCollection = new Collection(stationFeatures);

      // Snap 交互只使用站点要素
      const snap = new Snap({
        features: stationCollection, // 只吸附到站点
        pixelTolerance: 15,
      });

      mapElement.current.addInteraction(modify);
      mapElement.current.addInteraction(snap);
      modifyRef.current = modify;
      setIsEditing(true);
    } else {
      if (modifyRef.current) {
        const lineLayers = metroLayer.flatMap((layer) =>
          layer instanceof LayerGroup
            ? layer
                .getLayers()
                .getArray()
                .filter(
                  (l) =>
                    l instanceof VectorLayer &&
                    l.getSource()?.getFeatures()[0]?.getGeometry() instanceof LineString,
                )
            : [],
        );

        const lineCoordinates = lineLayers
          .map((layer) => {
            const vectorLayer = layer as VectorLayer<VectorSource>;
            const feature = vectorLayer.getSource()?.getFeatures()[0];
            const geometry = feature?.getGeometry() as LineString;
            if (!geometry) return null;
            return geometry.getCoordinates().map((coord) => toLonLat(coord));
          })
          .filter(Boolean);

        if (lineCoordinates.length > 0 && lineCoordinates[0]) {
          onLineEdit?.(lineCoordinates[0]);
        }

        mapElement.current.removeInteraction(modifyRef.current);
        // 移除 snap 交互
        const snapInteraction = mapElement.current
          .getInteractions()
          .getArray()
          .find((interaction) => interaction instanceof Snap);
        if (snapInteraction) {
          mapElement.current.removeInteraction(snapInteraction);
        }
        modifyRef.current = null;
      }
      setIsEditing(false);
    }
  }, [isEditing, lines, metroLayer, onLineEdit]);

  const handleReset = useCallback(() => {
    onLineEdit?.([]);
  }, [onLineEdit]);

  useEffect(() => {
    if (mapElement.current) {
      mapElement.current.dispose();
    }
    renderMap();

    return () => {
      if (modifyRef.current) {
        mapElement.current?.removeInteraction(modifyRef.current);
        setIsEditing(false);
      }

      if (mapElement.current) {
        mapElement.current.dispose();
      }
    };
  }, [renderMap]);

  return (
    <div
      style={{
        position: 'relative',
      }}
    >
      {isSingle && (
        <div
          style={{
            position: 'absolute',
            top: '10px',
            left: '10px',
            zIndex: 1000,
            display: 'flex',
            flexDirection: 'column',
            gap: '10px',
          }}
        >
          <button onClick={toggleEditMode}>{isEditing ? '退出编辑' : '编辑线段'}</button>
          <button onClick={handleReset}>{'重置'}</button>
        </div>
      )}
      <div id={`map-${unique_id}`} style={{ height: '700px' }} />
    </div>
  );
};

export default MapTianditu;
